package com.moss.cloud.base.biz.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.moss.cloud.base.api.model.from.AddMenuFrom;
import com.moss.cloud.base.api.model.from.BaseFrom;
import com.moss.cloud.base.api.model.from.EditMenuFrom;
import com.moss.cloud.base.biz.event.ResourceChangeEvent;
import com.moss.cloud.base.biz.mapper.SysMenuMapper;
import com.moss.cloud.base.biz.service.ISysMenuService;
import com.moss.cloud.base.biz.service.ISysRoleMenuService;
import com.moss.cloud.common.base.model.po.SysMenu;
import com.moss.cloud.common.base.model.po.SysRoleMenu;
import com.moss.cloud.common.core.constant.RoleCode;
import com.moss.cloud.common.core.enums.ChangeTypeEnum;
import com.moss.cloud.common.core.enums.state.Deleted;
import com.moss.cloud.common.core.exception.SystemErrorType;
import com.moss.cloud.common.core.model.vo.ProTree;
import com.moss.cloud.common.core.utils.BooleanHandel;
import com.moss.cloud.common.core.utils.SpringContextHolder;
import com.moss.cloud.common.core.utils.TreeUtil;
import com.moss.cloud.common.core.utils.UserContextHolder;
import com.moss.cloud.common.security.annotation.RequiresRole;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * 菜单业务实现
 *
 * @author 瑾年
 * @date 2023年4月16日
 */
@Slf4j
@Service
public class SysMenuServiceImpl extends ServiceImpl<SysMenuMapper, SysMenu> implements ISysMenuService {

    private ISysRoleMenuService sysRoleMenuService;

    public SysMenuServiceImpl(ISysRoleMenuService sysRoleMenuService) {
        this.sysRoleMenuService = sysRoleMenuService;
    }

    /**
     * 查询当前登录人的菜单树
     *
     * @return
     */
    @Override
    @RequiresRole
    public List<ProTree> menuTree() {
        List<Long> roleIds = UserContextHolder.getRoleIds();
        List<Long> menuIds = sysRoleMenuService.roleMenuIds(roleIds);
        // 该角色用户无菜单无权查看数据
        BooleanHandel.trueThrow(CollectionUtils.isEmpty(menuIds)).throwMessage(SystemErrorType.NO_MENU);
        List<SysMenu> sysMenus = this.list(new QueryWrapper<SysMenu>().lambda().in(SysMenu::getId, menuIds)
                .in(SysMenu::getDeleted, Deleted.ENABLE.code()).orderByAsc(SysMenu::getOrderNum));
        List<ProTree> proTrees = CollectionUtils.isNotEmpty(sysMenus) ? sysMenus.stream().map(item -> {
            ProTree proTree = new ProTree();
            BeanUtils.copyProperties(item, proTree);
            proTree.setKey(item.getId());
            proTree.setLabel(item.getName());
            return proTree;
        }).collect(Collectors.toList()) : Collections.emptyList();
        return TreeUtil.toTree(proTrees, 0L, ProTree::getKey, ProTree::getParentId, ProTree::setChildren);
    }

    /**
     * 查询用户角色数据
     *
     * @param roleIds
     * @return
     */
    @Override
    public List<SysMenu> userSysMenus(List<Long> roleIds) {
        List<Long> menuIds = sysRoleMenuService.roleMenuIds(roleIds);
        // 该角色用户无菜单无权查看数据
        BooleanHandel.trueThrow(CollectionUtils.isEmpty(menuIds)).throwMessage(SystemErrorType.NO_MENU);
        return this.list(new QueryWrapper<SysMenu>().lambda().in(SysMenu::getId, menuIds)
                .in(SysMenu::getDeleted, Deleted.ENABLE.code()).orderByAsc(SysMenu::getOrderNum));
    }

    /**
     * 获取菜单信息
     *
     * @param baseFrom
     * @return
     */
    @Override
    public SysMenu menuInfo(BaseFrom baseFrom) {
        return this.getById(baseFrom.getId());
    }

    /**
     * 编辑菜单
     *
     * @param editMenuFrom
     * @return
     */
    @Override
    @RequiresRole(codes = {RoleCode.SYSTEM_ADMIN})
    @Transactional(rollbackFor = Exception.class)
    public Boolean editSysMenu(EditMenuFrom editMenuFrom) {
        SysMenu sysMenu = this.getById(editMenuFrom.getId());
        BooleanHandel.trueThrow(Objects.isNull(sysMenu)).throwMessage(SystemErrorType.DATE_EMPTY);
        this.checkMenuPath(editMenuFrom.getPath(), sysMenu, Boolean.FALSE);
        SysMenu menu = new SysMenu();
        BeanUtils.copyProperties(editMenuFrom, menu);
        Boolean updateFlag = this.updateById(menu);
        // 发布一个资源更新事件
        SpringContextHolder.publishEvent(new ResourceChangeEvent(this, ChangeTypeEnum.SYS_MENU_CHANGE.getType()));
        return updateFlag;
    }

    /**
     * 添加菜单
     *
     * @param addMenuFrom
     * @return
     */
    @Override
    @RequiresRole(codes = {RoleCode.SYSTEM_ADMIN})
    @Transactional(rollbackFor = Exception.class)
    public Boolean addSysMenu(AddMenuFrom addMenuFrom) {
        List<Long> userRoleIds = UserContextHolder.getRoleIds();
        this.checkMenuPath(addMenuFrom.getPath(), new SysMenu(), Boolean.TRUE);
        SysMenu sysMenu = new SysMenu();
        BeanUtils.copyProperties(addMenuFrom, sysMenu);
        this.save(sysMenu);
        boolean saveFlag = ((SysMenuServiceImpl) AopContext.currentProxy()).saveBatchSysRoleMenu(sysMenu,userRoleIds);
        // 发布一个资源新增事件
        SpringContextHolder.publishEvent(new ResourceChangeEvent(this, ChangeTypeEnum.SYS_MENU_CHANGE.getType()));
        return saveFlag;
    }

    /**
     * 数据重复性校验
     *
     * @param path
     * @param sysMenu
     * @param isAdd
     */
    private void checkMenuPath(String path, SysMenu sysMenu, Boolean isAdd) {
        List<SysMenu> sysMenus = this.list(new QueryWrapper<SysMenu>().lambda().eq(SysMenu::getPath, path));
        BooleanHandel.trueThrow(isAdd.equals(Boolean.TRUE) && CollectionUtils.isNotEmpty(sysMenus)).throwMessage(SystemErrorType.MENU_PATH_EXIT);
        BooleanHandel.isTrue(isAdd.equals(Boolean.FALSE) && CollectionUtils.isNotEmpty(sysMenus)).trueHandle(() -> {
            sysMenus.removeIf(item -> item.getId().equals(sysMenu.getId()));
            BooleanHandel.trueThrow(CollectionUtils.isNotEmpty(sysMenus)).throwMessage(SystemErrorType.MENU_PATH_EXIT);
        });
    }

    @Transactional(rollbackFor = Exception.class)
    public boolean saveBatchSysRoleMenu(SysMenu sysMenu, List<Long> userRoleIds) {
        List<SysRoleMenu> sysRoleMenus = new ArrayList<>();
        userRoleIds.forEach(item -> {
            SysRoleMenu sysRoleMenu = new SysRoleMenu();
            sysRoleMenu.setRoleId(item);
            sysRoleMenu.setMenuId(sysMenu.getId());
            sysRoleMenus.add(sysRoleMenu);
        });
        return sysRoleMenuService.saveBatch(sysRoleMenus);
    }


    /**
     * 删除菜单
     *
     * @param baseFrom
     * @return
     */
    @Override
    public Boolean removeSysMenu(BaseFrom baseFrom) {
        SysMenu sysMenu = this.getById(baseFrom.getId());
        BooleanHandel.trueThrow(Objects.isNull(sysMenu)).throwMessage(SystemErrorType.DATE_EMPTY);
        long count = this.count(new QueryWrapper<SysMenu>().lambda().eq(SysMenu::getParentId, sysMenu.getId()));
        BooleanHandel.trueThrow(count > 0).throwMessage(SystemErrorType.CHILDREN_NOT_EMPTY);
        Boolean removeFlag = this.removeById(baseFrom.getId());
        // 发布一个资源更新事件
        SpringContextHolder.publishEvent(new ResourceChangeEvent(this, ChangeTypeEnum.SYS_MENU_CHANGE.getType()));
        return removeFlag;
    }

    /**
     * 刷新资源池
     *
     * @return
     */
    @Override
    public boolean refreshSysMenu() {
        // 发布一个资源更新事件
        SpringContextHolder.publishEvent(new ResourceChangeEvent(this, ChangeTypeEnum.SYS_MENU_CHANGE.getType()));
        return Boolean.TRUE;
    }


}
